﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nHydrate.Generator.Common.Util;
using Microsoft.VisualStudio.Modeling.Diagrams;

namespace nHydrate.Dsl
{
	partial class nHydrateDiagram
	{
		///Determine if this diagram is loading from disk
		public bool IsLoading
		{
			get { return _isLoading; }
			set { _isLoading = value; }
		}
		private bool _isLoading = false;

		#region Constructors
		 //Constructors were not generated for this class because it had HasCustomConstructor
		 //set to true. Please provide the constructors below in a partial class.
		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="store">Store where new element is to be created.</param>
		/// <param name="propertyAssignments">List of domain property id/value pairs to set once the element is created.</param>
		public nHydrateDiagram(Microsoft.VisualStudio.Modeling.Store store, params Microsoft.VisualStudio.Modeling.PropertyAssignment[] propertyAssignments)
			: this(store != null ? store.DefaultPartitionForClass(DomainClassId) : null, propertyAssignments)
		{
		}

		private bool CanUndoRedoCallback(bool isUndo, Microsoft.VisualStudio.Modeling.TransactionItem transactionItem)
		{
			//This callback will catch the UNDO events and if the UNDO was a factor 
			//then remove it from the refactor collection that is used later for SQL emits
			var model = this.ModelElement as nHydrateModel;
			var refactor = model.Refactorizations.FirstOrDefault(x => x.Id == transactionItem.Id);
			if (refactor != null) model.Refactorizations.Remove(refactor);
			return true;
		}

		public Microsoft.VisualStudio.Modeling.CanCommitResult CanCommitCallback(Microsoft.VisualStudio.Modeling.Transaction transaction)
		{
			return Microsoft.VisualStudio.Modeling.CanCommitResult.Commit;
		}
		
		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="partition">Partition where new element is to be created.</param>
		/// <param name="propertyAssignments">List of domain property id/value pairs to set once the element is created.</param>
		public nHydrateDiagram(Microsoft.VisualStudio.Modeling.Partition partition, params Microsoft.VisualStudio.Modeling.PropertyAssignment[] propertyAssignments)
			: base(partition, propertyAssignments)
		{
			this.Store.UndoManager.AddCanUndoRedoCallback(CanUndoRedoCallback);
			this.Store.TransactionManager.AddCanCommitCallback(CanCommitCallback);
			nHydrate.Generator.Common.GeneratorFramework.AddinAppData.Instance.Version = nHydrate.Generator.Common.GeneratorFramework.AddinAppData.nHydrateVersion.v5;

			//Custom code so need to override the constructor
			this.ShowGrid = true;
			this.DisplayType = true;
			this.IsLoading = true;
			this.DiagramAdded += new EventHandler<Microsoft.VisualStudio.Modeling.ElementAddedEventArgs>(nHydrateDiagram_DiagramAdded);
			//this.ElementAdded += new EventHandler<Microsoft.VisualStudio.Modeling.ElementAddedEventArgs>(nHydrateDiagram_ElementAdded);

		}

		#endregion

		#region Events
		protected override void OnElementDeleted(Microsoft.VisualStudio.Modeling.ElementDeletedEventArgs e)
		{
			base.OnElementDeleted(e);
		}

		public override void OnBeginEdit(DiagramItemEventArgs e)
		{
			base.OnBeginEdit(e);
		}

		protected override void OnElementAdded(Microsoft.VisualStudio.Modeling.ElementAddedEventArgs e)
		{
			var model = this.ModelElement as nHydrate.Dsl.nHydrateModel;
			if (!model.IsLoading)
			{
				if (e.ModelElement is StoredProcedure)
				{
					using (var transaction = model.Store.TransactionManager.BeginTransaction(Guid.NewGuid().ToString()))
					{
						(e.ModelElement as StoredProcedure).PrecedenceOrder = ++model.MaxPrecedenceOrder;
						transaction.Commit();
					}
				}
				else if (e.ModelElement is View)
				{
					using (var transaction = model.Store.TransactionManager.BeginTransaction(Guid.NewGuid().ToString()))
					{
						(e.ModelElement as View).PrecedenceOrder = ++model.MaxPrecedenceOrder;
						transaction.Commit();
					}
				}
				else if (e.ModelElement is Function)
				{
					using (var transaction = model.Store.TransactionManager.BeginTransaction(Guid.NewGuid().ToString()))
					{
						(e.ModelElement as Function).PrecedenceOrder = ++model.MaxPrecedenceOrder;
						transaction.Commit();
					}
				}
			}

			base.OnElementAdded(e);
		}

		public event EventHandler<ModelElementEventArgs> ShapeDoubleClick;

		protected void OnShapeDoubleClick(ModelElementEventArgs e)
		{
			if (this.ShapeDoubleClick != null)
				ShapeDoubleClick(this, e);
		}

		public event EventHandler<ModelElementEventArgs> ShapeConfiguring;

		protected void OnShapeConfiguring(ModelElementEventArgs e)
		{
			if (this.ShapeConfiguring != null)
				ShapeConfiguring(this, e);
		}

		#endregion

		#region Event Handlers

		private void FieldAdded(object sender, Microsoft.VisualStudio.Modeling.ElementAddedEventArgs e)
		{
			var field = e.ModelElement as Field;
			if (field.Entity == null) return;
			if (field.Entity.nHydrateModel == null) return;

			if (!field.Entity.nHydrateModel.IsLoading)
			{
				var maxSortOrder = 1;
				if (field.Entity.Fields.Count > 0)
					maxSortOrder = field.Entity.Fields.Max(x => x.SortOrder);

				using (var transaction = this.Store.TransactionManager.BeginTransaction(Guid.NewGuid().ToString()))
				{
					field.SortOrder = ++maxSortOrder;
					transaction.Commit();
				}
			}
		}

		private void IndexColumnAdded(object sender, Microsoft.VisualStudio.Modeling.ElementAddedEventArgs e)
		{
			var ic = e.ModelElement as IndexColumn;
			if (ic.Index == null) return;
			if (ic.Index.Entity == null) return;
			if (ic.Index.Entity.nHydrateModel == null) return;

			if (!ic.IsInternal && !ic.Index.Entity.nHydrateModel.IsLoading)
			{
				if (!ic.Index.Entity.nHydrateModel.IsLoading)
				{
					if (ic.Index.IndexType != IndexTypeConstants.User)
						throw new Exception("This is a managed index and cannot be modified.");
				}
			}

			if (!ic.Index.Entity.nHydrateModel.IsLoading)
			{
				if (ic.SortOrder == 0)
				{
					using (var transaction = this.Store.TransactionManager.BeginTransaction(Guid.NewGuid().ToString()))
					{
						var max = 0;
						if (ic.Index.IndexColumns.Count > 0)
							max = ic.Index.IndexColumns.Max(x => x.SortOrder);
						ic.SortOrder = max + 1;
						transaction.Commit();
					}
				}
			}

		}

		private void FieldPropertyChanged(object sender, Microsoft.VisualStudio.Modeling.ElementPropertyChangedEventArgs e)
		{
			var f = e.ModelElement as Field;
			f.CachedImage = null;
		}

		private void DiagramViewablePropertyChanged(object sender, Microsoft.VisualStudio.Modeling.ElementPropertyChangedEventArgs e)
		{
			var model = e.ModelElement as nHydrateModel;
			if (model != null)
			{
				if (e.DomainProperty.Name == "ShowStoredProcedures")
				{
					var list = this.Store.ElementDirectory.AllElements
					.Where(x => x is StoredProcedureShape)
					.ToList()
					.Cast<StoredProcedureShape>()
					.ToList();

					var visible = (bool)e.NewValue;
					if (visible) list.ForEach(x => x.Show());
					else list.ForEach(x => x.Hide());

					//Arrange
					this.AutoLayoutShapeElements(
						list,
						Microsoft.VisualStudio.Modeling.Diagrams.GraphObject.VGRoutingStyle.VGRouteTreeNS,
						Microsoft.VisualStudio.Modeling.Diagrams.GraphObject.PlacementValueStyle.VGPlaceNS,
						true);
				}

				if (e.DomainProperty.Name == "ShowViews")
				{
					var list = this.Store.ElementDirectory.AllElements
					.Where(x => x is ViewShape)
					.ToList()
					.Cast<ViewShape>()
					.ToList();

					var visible = (bool)e.NewValue;
					if (visible) list.ForEach(x => x.Show());
					else list.ForEach(x => x.Hide());

					//Arrange
					this.AutoLayoutShapeElements(
						list,
						Microsoft.VisualStudio.Modeling.Diagrams.GraphObject.VGRoutingStyle.VGRouteTreeNS,
						Microsoft.VisualStudio.Modeling.Diagrams.GraphObject.PlacementValueStyle.VGPlaceNS,
						true);
				}

				if (e.DomainProperty.Name == "ShowFunctions")
				{
					var list = this.Store.ElementDirectory.AllElements
					.Where(x => x is FunctionShape)
					.ToList()
					.Cast<FunctionShape>()
					.ToList();

					var visible = (bool)e.NewValue;
					if (visible) list.ForEach(x => x.Show());
					else list.ForEach(x => x.Hide());

					//Arrange
					this.AutoLayoutShapeElements(
						list,
						Microsoft.VisualStudio.Modeling.Diagrams.GraphObject.VGRoutingStyle.VGRouteTreeNS,
						Microsoft.VisualStudio.Modeling.Diagrams.GraphObject.PlacementValueStyle.VGPlaceNS,
						true);
				}
			}
		}

		private void nHydrateDiagram_DiagramAdded(object sender, Microsoft.VisualStudio.Modeling.ElementAddedEventArgs e)
		{
			var model = this.Diagram.ModelElement as nHydrateModel;
			if (model.ShowStoredProcedures)
				this.Store.ElementDirectory.AllElements.Where(x => x is StoredProcedureShape).ToList().Cast<StoredProcedureShape>().ToList().ForEach(x => x.Show());
			else
				this.Store.ElementDirectory.AllElements.Where(x => x is StoredProcedureShape).ToList().Cast<StoredProcedureShape>().ToList().ForEach(x => x.Hide());

			if (model.ShowViews)
				this.Store.ElementDirectory.AllElements.Where(x => x is ViewShape).ToList().Cast<ViewShape>().ToList().ForEach(x => x.Show());
			else
				this.Store.ElementDirectory.AllElements.Where(x => x is ViewShape).ToList().Cast<ViewShape>().ToList().ForEach(x => x.Hide());

			if (model.ShowFunctions)
				this.Store.ElementDirectory.AllElements.Where(x => x is FunctionShape).ToList().Cast<FunctionShape>().ToList().ForEach(x => x.Show());
			else
				this.Store.ElementDirectory.AllElements.Where(x => x is FunctionShape).ToList().Cast<FunctionShape>().ToList().ForEach(x => x.Hide());

			//Notify when field is changed so we can refresh icon
			this.Store.EventManagerDirectory.ElementPropertyChanged.Add(
				this.Store.DomainDataDirectory.FindDomainClass(typeof(Field)),
				new EventHandler<Microsoft.VisualStudio.Modeling.ElementPropertyChangedEventArgs>(FieldPropertyChanged));

			this.Store.EventManagerDirectory.ElementPropertyChanged.Add(
				this.Store.DomainDataDirectory.FindDomainClass(typeof(nHydrateModel)),
				new EventHandler<Microsoft.VisualStudio.Modeling.ElementPropertyChangedEventArgs>(DiagramViewablePropertyChanged));

			//Notify when an index column is added
			this.Store.EventManagerDirectory.ElementAdded.Add(
				this.Store.DomainDataDirectory.FindDomainClass(typeof(IndexColumn)),
				new EventHandler<Microsoft.VisualStudio.Modeling.ElementAddedEventArgs>(IndexColumnAdded));

			//Notify when a FIeld is added
			this.Store.EventManagerDirectory.ElementAdded.Add(
				this.Store.DomainDataDirectory.FindDomainClass(typeof(Field)),
				new EventHandler<Microsoft.VisualStudio.Modeling.ElementAddedEventArgs>(FieldAdded));

			this.IsLoading = false;
			model.IsDirty = true;

		}

		//private void nHydrateDiagram_ElementAdded(object sender, Microsoft.VisualStudio.Modeling.ElementAddedEventArgs e)
		//{
		//  ////When adding a module, ensure there is a RelationModule for all relations in the model
		//  //if (e.ModelElement is Module)
		//  //{
		//  //  var model = this.ModelElement as nHydrateModel;
		//  //  using (var transaction = model.Store.TransactionManager.BeginTransaction(Guid.NewGuid().ToString()))
		//  //  {
		//  //    var module = e.ModelElement as Module;
		//  //    foreach (var relation in module.nHydrateModel.AllRelations)
		//  //    {
		//  //      if (module.nHydrateModel.RelationModules.Count(x => x.RelationID == relation.Id && x.ModuleId == module.Id) == 0)
		//  //        module.nHydrateModel.RelationModules.Add(new RelationModule(this.Partition) { ModuleId = module.Id, RelationID = relation.Id, Included = false, IsEnforced = true });
		//  //    }
		//  //    transaction.Commit();
		//  //  }

		//  //}

		//  ////When adding a relation, ensure there is a RelationModule for all modules in the model
		//  //if (e.ModelElement is EntityHasEntities)
		//  //{
		//  //  var model = this.ModelElement as nHydrateModel;
		//  //  using (var transaction = model.Store.TransactionManager.BeginTransaction(Guid.NewGuid().ToString()))
		//  //  {
		//  //    var relation = e.ModelElement as EntityHasEntities;
		//  //    foreach (var module in model.Modules)
		//  //    {
		//  //      if (module.nHydrateModel.RelationModules.Count(x => x.RelationID == relation.Id && x.ModuleId == module.Id) == 0)
		//  //        module.nHydrateModel.RelationModules.Add(new RelationModule(this.Partition) { ModuleId = module.Id, RelationID = relation.Id, Included = false, IsEnforced = true });
		//  //    }
		//  //    transaction.Commit();
		//  //  }

		//  //}

		//}

		#endregion

		protected override ShapeElement CreateChildShape(Microsoft.VisualStudio.Modeling.ModelElement element)
		{
			var shape = base.CreateChildShape(element);

			var model = this.ModelElement as nHydrate.Dsl.nHydrateModel;
			if (model != null)
			{
				if (shape is StoredProcedureShape)
				{
					if (model.ShowStoredProcedures) shape.Show();
					else shape.Hide();
				}
				else if (shape is ViewShape)
				{
					if (model.ShowViews) shape.Show();
					else shape.Hide();
				}
				else if (shape is FunctionShape)
				{
					if (model.ShowFunctions) shape.Show();
					else shape.Hide();
				}

			}

			return shape;
		}

		protected override void OnChildConfiguring(Microsoft.VisualStudio.Modeling.Diagrams.ShapeElement child, bool createdDuringViewFixup)
		{
			base.OnChildConfiguring(child, createdDuringViewFixup);

			try
			{
				if (!this.IsLoading)
				{
					//Add a default field to entities
					if (child.ModelElement is Entity)
					{
						var entity = child.ModelElement as Entity;
						if (entity.Fields.Count == 0)
						{
							entity.Fields.Add(new Field(entity.Partition)
							{
								DataType = DataTypeConstants.Int,
								Identity = IdentityTypeConstants.Database,
								IsPrimaryKey = true,
								Name = "ID",
							});
						}
					}

					this.OnShapeConfiguring(new ModelElementEventArgs() { Shape = child });
				}

			}
			catch (Exception ex)
			{
				throw;
			}
		}

		internal void NotifyShapeDoubleClick(Microsoft.VisualStudio.Modeling.Diagrams.ShapeElement shape)
		{
			this.OnShapeDoubleClick(new ModelElementEventArgs() { Shape = shape });
		}

		protected override void InitializeResources(Microsoft.VisualStudio.Modeling.Diagrams.StyleSet classStyleSet)
		{
			var dte = GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
			nHydrate.Generator.Common.Util.EnvDTEHelper.Instance.SetDTE(dte);
		}

		protected override bool CalculateDerivedVisible(ShapeElement source)
		{
			return base.CalculateDerivedVisible(source);
		}

		public override bool CanShapeContainConnectors(ShapeElement parentCandidate)
		{
			return base.CanShapeContainConnectors(parentCandidate);
		}

		public override void CoerceSelection(DiagramItem item, DiagramClientView view, bool isAddition)
		{
			base.CoerceSelection(item, view, isAddition);
		}

		protected override ShapeElement DetermineHighlightShape(ShapeElement shape)
		{
			return base.DetermineHighlightShape(shape);
		}
		public override void DrawResizeFeedback(DiagramPaintEventArgs e, RectangleD bounds)
		{
			base.DrawResizeFeedback(e, bounds);
		}

		public override ShapeElement FixUpChildShapes(Microsoft.VisualStudio.Modeling.ModelElement childElement)
		{
			return base.FixUpChildShapes(childElement);
		}
		public override System.Collections.IList FixUpDiagramSelection(ShapeElement newChildShape)
		{
			return base.FixUpDiagramSelection(newChildShape);
		}

		public override System.Collections.ICollection GetChildElements(Microsoft.VisualStudio.Modeling.ModelElement parentElement)
		{
			return base.GetChildElements(parentElement);
		}

		//protected override void InitializeShapeFields(IList<Microsoft.VisualStudio.Modeling.Diagrams.ShapeField> shapeFields)
		//{
		//  base.InitializeShapeFields(shapeFields);

		//  var assembly = System.Reflection.Assembly.GetExecutingAssembly();
		//  var imageStream = assembly.GetManifestResourceStream(assembly.GetName().Name + ".Resources.nhydrate-background.png");
		//  var backgroundField = new ImageField("background", new System.Drawing.Bitmap(imageStream));

		//  backgroundField.DefaultFocusable = false;
		//  backgroundField.DefaultSelectable = false;
		//  backgroundField.DefaultVisibility = true;
		//  shapeFields.Add(backgroundField);

		//  backgroundField.AnchoringBehavior.SetTopAnchor(AnchoringBehavior.Edge.Top, 0.01);
		//  backgroundField.AnchoringBehavior.SetTopAnchor(AnchoringBehavior.Edge.Left, 0.01);
		//  backgroundField.AnchoringBehavior.SetTopAnchor(AnchoringBehavior.Edge.Right, 0.01);
		//  backgroundField.AnchoringBehavior.SetTopAnchor(AnchoringBehavior.Edge.Bottom, 0.01);
		//}

	}
}
